/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package compiler.grammar;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

/**
 *
 * @author meemo
 */
public class NonTerminal extends Symbol
{
    //~ Static fields/initializers ---------------------------------------------

    private static int NEXT_ID = 0;

    //~ Instance fields --------------------------------------------------------

    Set<Terminal>                       firstSet = new HashSet<Terminal>();
    private final ArrayList<Production> productions;
    private final String                name;
    private final int                   id = nextId();
    private boolean                     nullable = false;

    //~ Constructors -----------------------------------------------------------

    public NonTerminal(String name, ArrayList<Production> productions)
    {
        this.name        = name;
        this.productions = productions;

        for (Production p : productions)
        {
            p.setNonTerminal(this);
        }
    }

    //~ Methods ----------------------------------------------------------------

    public static void reset()
    {
        NEXT_ID = 0;
    }

    @Override
    public boolean equals(Object obj)
    {
        if (obj == null)
        {
            return false;
        }

        if (getClass() != obj.getClass())
        {
            return false;
        }

        final NonTerminal other = (NonTerminal)obj;

        if ((this.name == null) ? (other.name != null) : (!this.name.equals(other.name)))
        {
            return false;
        }

        return true;
    }

    public Set<Terminal> getFirstSet()
    {
        return firstSet;
    }

    @Override
    public int getId()
    {
        return id;
    }

    @Override
    public String getName()
    {
        return name;
    }

    public ArrayList<Production> getProductions()
    {
        return productions;
    }

    @Override
    public int hashCode()
    {
        // this assumes the the names of terminals and non-terminals are in different namespacess
        return name.hashCode();
    }

    public boolean isNullable()
    {
        return nullable;
    }

    @Override
    public String toString()
    {
        return name;
    }

    /**
     * @return true if nullability changes
     */
    public boolean updateNullability()
    {
        if (isNullable()) // nullability can't go from true to false
        {
            return false;
        }

        for (Production prod : productions)
        {
            if (prod.updateNullability())
            {
                nullable = true;

                break;
            }
        }

        return isNullable();
    }

    protected static synchronized int nextId()
    {
        return NEXT_ID++; // starts with 0
    }
}
